#include "EItemManager.h"
#include "DefineCommon.h"
#include "EItem.h"
#include "EPlugin.h"
#include <csetjmp>
#include <string>
#include <EItemVisitor.h>

class VisitorFindItem : public EItemVisitor
{
public:
    VisitorFindItem(){}
    virtual ~VisitorFindItem(){}

    long targetTag = 0;
    EString targetName;
    EItemPtr targetItem;
    /*overwrite EItemVisitor's core function*/
    virtual bool handleEItem(EItemPtr item, int depth, EItemPtr parentItem)
    {
        if(item->getTag() == targetTag && targetTag != 0)
        {
            targetItem = item;
            //if found the target item, then return;
            return false;
        }
        
        if(item->getName() == targetName && targetName.isEmpty()==false)
        {
            targetItem = item;
            return false;
        }
        return true;
    }
protected:
};

class VisitorCountItemWithSameType : public EItemVisitor
{
public:
    VisitorCountItemWithSameType(){}
    virtual ~VisitorCountItemWithSameType(){}

    EString m_targetClassName;
    int m_count = 0;
    /*overwrite EItemVisitor's core function*/
    virtual bool handleEItem(EItemPtr item, int depth, EItemPtr parentItem)
    {
        if(item->getOrCreatMeta()->getClassName() == this->m_targetClassName)
        {
            m_count++;
        }
        return true;
    }
protected:
};

struct EItemManagerData
{
    /*The root of EItem that represents Meta Item which is registed by plugin*/
    EItemPtr m_itemTypeRoot;
    /*The root of EItem that represents actual EItem and is showing on MainWindow's WidgetTree.*/
    EItemPtr m_itemTreeRoot;
    /*The root of EItem that represents some setting item for global over this software.*/
    EItemPtr m_itemSettingRoot;

    
};

EItemManager* EItemManager::m_instance = nullptr;

EItemManager::EItemManager()
{
    m_data = new EItemManagerData;
    m_data->m_itemTypeRoot = NewSmartPtr(EItem)();
    m_data->m_itemTypeRoot->setName("TypeRoot");

    m_data->m_itemTreeRoot = NewSmartPtr(EItem)();
    m_data->m_itemTreeRoot->setName("Tree Root");

    m_data->m_itemSettingRoot = NewSmartPtr(EItem)();
    m_data->m_itemSettingRoot->setName("Setting Root");
}

EItemManager::~EItemManager()
{
    delete m_data;
}
    
EItemManager* EItemManager::instance()
{
    if(m_instance == nullptr)
    {
        m_instance = new EItemManager();
    }
    return m_instance;
}

void EItemManager::addToItemTypeRoot(EItemPtr itemType)
{
    m_data->m_itemTypeRoot->addChild(itemType);
}

EItemPtr EItemManager::getItemTypeRoot()
{
    return m_data->m_itemTypeRoot;
}

EItemPtr EItemManager::getItemTreeRoot()
{
    return m_data->m_itemTreeRoot;
}

EItemPtr EItemManager::getItemSettingRoot()
{
    return m_data->m_itemSettingRoot;
}

EItemPtr EItemManager::newItem(const EString& itemTypeName)
{
    EItemPtr typeItem = this->getItemByName(this->m_data->m_itemTypeRoot, itemTypeName);
    return this->newItem(typeItem);
}

EItemPtr EItemManager::newItem(long tagTypeItem)
{
    EItemPtr typeItem = this->getItemByTag(this->m_data->m_itemTypeRoot, tagTypeItem);
    return this->newItem(typeItem);
}

EItemPtr EItemManager::newItem(EItemPtr typeItemPtr)
{
    EItemPtr newItemReturn;
    if(typeItemPtr != nullptr)
    {
        EString funName = typeItemPtr->getName() + "_Fun_factory";
        EItemMetaFunctionPtr fun = typeItemPtr->getOrCreatMeta()->getFunction(funName);
        if(fun != nullptr)
        {
            // ELOG_DEBUG("funName=%s", fun->getName().data());
            EVariant result;
            fun->call(&result);
            EItem* newItemRawPtr = (EItem*)result.to_intptr_t();
            EItemPtr newItemTemp(newItemRawPtr);
            newItemReturn = newItemTemp;
        }
    }
    
    if(newItemReturn != nullptr)
    {
        //set a default name
        EString className = newItemReturn->getOrCreatMeta()->getClassName();
        EString name = className
                      + "_" 
                      + EString::number(this->checkItemTypeCount(className));
        newItemReturn->setName(name.data());
    }

    return newItemReturn;
}

EItemPtr EItemManager::getItemByTag(EItemPtr rootItem, long tag)
{
    VisitorFindItem visitor;
    visitor.targetTag = tag;
    visitor.targetName = "";
    visitor.travelEItem(rootItem);
    return visitor.targetItem;
}
    
EItemPtr EItemManager::getItemByName(EItemPtr rootItem, const EString& name)
{
    VisitorFindItem visitor;
    visitor.targetTag = 0;
    visitor.targetName = name;
    visitor.travelEItem(rootItem);
    return visitor.targetItem;
}

int EItemManager::checkItemTypeCount(const EString& className)
{
    VisitorCountItemWithSameType visitor;
    visitor.m_targetClassName = className;
    visitor.travelEItem(this->m_data->m_itemTreeRoot);

    return visitor.m_count;
}
